﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;

namespace Tzync
{
    /*****************************************************************************/
    /* Facade Class of our Software. Allocates responsibilities to other classes */
    /*****************************************************************************/

    public class SyncTool
    {
        //Data Members of the SyncTool Class
        private Analyze analyzer;
        private MetaData metaDataHandler;
        private List<Difference> differences;
        private List<TreeNode> arrNodes;
        private SystemType systemName;
        private bool secondTime, firstTime;        
        private ISubject differenceSubject;
        private Rollback rollbackHandler;
        private String cpuID, rootAddress;
        private ProfileSystem profileSystemHandler;
        private Profile profile;
        private Filter filter;              

        public SyncTool(String newCpuId)
        {
            //Assigning local variables to Values from parameters
            cpuID = newCpuId;
            profileSystemHandler = new ProfileSystem(cpuID);

            arrNodes = new List<TreeNode>();
            firstTime = false;
            differenceSubject = new DifferenceSubject();   
            filter = new Filter();
        }

        public void IncludeFlag(bool includeFlag)
        {
            filter.IncludeFlag(includeFlag);
        }

        public void AddFileFilter(String s)
        {
            filter.AddFileFilter(s);
        }
        public void AddAdvancedFilter(String s)
        {
            filter.AddAdvancedFilter(s);
        }

        public List<string> getFileFilter()
        {
            return filter.getFileFilters();
        }

        public List<string> getAdvancedFilter()
        {
            return filter.getAdvancedFilters();
        }

        public void PerformFilter()
        {
            filter.PerformFilter(this.GetDifferences());
        }
        public void FlushFilter()
        {
            filter.flush();
        }

        public List<Difference> GetDifferences()
        {
            return differences;
        }

        public bool GetFirstTime()
        {
            return firstTime;
        }

        public bool GetSecondTime()
        {
            return secondTime;
        }        

        public List<TreeNode> GetArrNodes()
        {
            return this.arrNodes;
        }

        public ISubject GetDifferenceSubject()
        {
            return differenceSubject;
        }

        public void RegisterObserver(IObserver observer)
        {
            differenceSubject.RegisterObject(observer);     //register UI as an observerable object
        }

        public void CreateProfile(String newProfileName, String newRootAddress)
        {
            profile = profileSystemHandler.CreateProfile(newProfileName, newRootAddress);
            SetupSystemVariables();
        }

        public List<String> GetAvailableProfiles()
        {
            return profileSystemHandler.GetAvailableProfiles();
        }

        public void WriteProfile()
        {
            profileSystemHandler.WriteProfile();
            CreateRollback(GetProfile());
        }

        //This function is called at the first sync at System B
        //To add entries in SystemData in the profile

        public void ModifyProfile(Profile p, String rootAddress)
        {
            profile = profileSystemHandler.ModifyProfile(p, rootAddress);
            SetupSystemVariables();
            CreateRollback(GetProfile());
        }

        public void LoadProfile(String newProfileName)
        {
            profile = profileSystemHandler.GetProfile(newProfileName);
            SetupSystemVariables();
            LoadMetadata();
            CreateRollback(GetProfile());
        }

        private void SetupSystemVariables()
        {
            //Setting local variables from parameters
            firstTime = profile.FirstTime;
            secondTime = profile.SecondTime;
            systemName = profile.SystemName;
            if (systemName == SystemType.Host)
                rootAddress = profile.RootAddressA;
            else
                rootAddress = profile.RootAddressB;
        }

        public Profile GetProfile()
        {
            return profile;
        }        

        public void RemoveProfile(String newProfileName)
        {
            profileSystemHandler.RemoveProfile(newProfileName);
            profileSystemHandler.WriteProfile();
            RemoveRollback(newProfileName);
        }

        //This function is called when the root folder of the current job is moved/renamed

        public void SetRootPath(String path)
        {
            if (systemName == SystemType.Host)
            {
                UpdateRollback(profile.RootAddressA, path);
                arrNodes[0].setFullPathA(path);
                profile.RootAddressA = path;
                profile.WriteSystem();
                rootAddress = path;

            }
            else if (systemName == SystemType.Guest)
            {
                UpdateRollback(profile.RootAddressB, path);
                arrNodes[0].setFullPathB(path);
                profile.RootAddressB = path;
                profile.WriteSystem();
                rootAddress = path;
            }
        }        

        public void LoadMetadata()
        {
            //Loading Metadata            
            metaDataHandler = new MetaData(profile);
            metaDataHandler.readFromFile();
            arrNodes = metaDataHandler.getAllNodes();
        }        

        public void Analyze()
        {
            DirectoryInfo D = new DirectoryInfo(@rootAddress);
            analyzer = new Analyze(arrNodes, secondTime, systemName, differenceSubject, ref filter);
            
            try
            {
                differences = analyzer.GetDifferences(D);                
            }
            catch (InvalidPathException)
            {
                throw;
            }
            catch (ArgumentNullException)
            {
                throw;
            }
        }

        public SystemType GetSystemName()
        {
            return systemName;
        }

        public String GetRootAddress()
        {
            return rootAddress;
        }

        public String GetCpuID()
        {
            return cpuID;
        }

        public void SetCpuID(String str)
        {
            cpuID = str;
        }

        public void Sync()
        {
            Sync sync;            
            if (firstTime) // for first time syncing
            {
                if (differences == null || differences.Count == 0)
                {
                    DirectoryInfo source = new DirectoryInfo(rootAddress);
                    sync = new Sync(arrNodes, secondTime, systemName, profile.ProfilePath);
                    sync.CopyFilesToHD(source);
                }
                else // user has synced and made changes. Then will only sync the differences
                {
                    sync = new Sync(differences, arrNodes, systemName, profile.ProfilePath);
                    sync.Reconcile();
                }
                firstTime = false;
            }
            else if (secondTime) //copy files to system and then call analyze to copy changes
            {
                DirectoryInfo destination = new DirectoryInfo(rootAddress);
                sync = new Sync(arrNodes, secondTime, systemName, profile.ProfilePath);
                rootAddress = sync.CopyFilesToSystem(destination);
                Analyze();
                if (differences != null && differences.Count != 0)
                {
                    sync = new Sync(differences, arrNodes, systemName, profile.ProfilePath);
                    sync.Reconcile();
                }
                secondTime = false;
            }
            else // norml syncing
            {
                rollbackHandler.AddRollback(differences, arrNodes, systemName);  //adds to rollback files                
                sync = new Sync(differences, arrNodes, systemName, profile.ProfilePath);                
                sync.Reconcile();
            }
            differences = null;            
            metaDataHandler.writeToFile();
        }

        //initializes rollback
        public Rollback CreateRollback(Profile newProfile)
        {
            rollbackHandler = new Rollback(newProfile);
            return rollbackHandler;
        }

        //perform rollback
        public void ExecuteRollback(List<String> PathList)
        {
            rollbackHandler.PerformRollback(PathList, arrNodes, systemName);
            metaDataHandler.writeToFile();
        }

        //removes profile from rollback
        public void RemoveRollback(String profileName)
        {
            Rollback newRoller = new Rollback();
            newRoller.RemoveRollback(profileName);
        }

        //updates rollback
        public void UpdateRollback(String oldRootAddress, String newRootAddress)
        {
            rollbackHandler.UpdateRollbackFile(oldRootAddress, newRootAddress);
        }

        //returns a list of rollback files
        public List<RollbackFile> GetRollbackFileList()
        {
            return rollbackHandler.GetRollbackList();
        }

        //returns space allocated to rollback on computer
        public long GetRollbackSpaceAllocated()
        {
            return rollbackHandler.GetSpaceAllocated();
        }

        //returns space left for rollback on computer
        public long GetRollbackSpaceRemaining()
        {
            return rollbackHandler.GetSpaceRemaining();
        }

        //resolves file/folder conflicts
        public void ResolveConflicts(List<Difference> conflicts)
        {
            rollbackHandler.AddRollback(conflicts, arrNodes, systemName);  //adds to rollback files
            Sync sync = new Sync(conflicts, arrNodes, systemName, profile.ProfilePath);
            sync.Reconcile();
            metaDataHandler.writeToFile();  //write to metadata
        }

    }

    /***********************/
    /* Class SyncTool ends */
    /***********************/
}
